home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
INFO
/
TSFAQP18.ZIP
/
FAQPAS2.TXT
< prev
next >
Wrap
Internet Message Format
|
1993-12-26
|
56KB
From ts@uwasa.fi Sun Dec 26 00:00:00 1993
Subject: FAQPAS2.TXT contents
Copyright (c) 1993 by Timo Salmi
All rights reserved
FAQPAS2.TXT More frequently (and not so frequently) asked Turbo
Pascal questions with Timo's answers. The items are in no particular
order.
You are free to quote brief passages from this file provided you
clearly indicate the source with a proper acknowledgment.
Comments and corrections are solicited. But if you wish to have
individual Turbo Pascal consultation, please rather post your
questions to a suitable UseNet newsgroup like comp.lang.pascal. It
is much more efficient than asking me by email. I'd like to help,
but I am very pressed for time. I prefer to pick the questions I
answer from the Usenet news. Thus I can answer publicly at one go if
I happen to have an answer. Besides, newsgroups have a number of
readers who might know a better or an alternative answer. Don't be
discouraged, though, if you get a reply like this from me. I am
always glad to hear from fellow Turbo Pascal users.
..................................................................
Prof. Timo Salmi Co-moderator of comp.archives.msdos.announce
Moderating at garbo.uwasa.fi anonymous FTP archives 128.214.87.1
Faculty of Accounting & Industrial Management; University of Vaasa
Internet: ts@uwasa.fi BBS +(358)-61-3170972; FIN-65101, Finland
-------------------------------------------------------------------
31) How does one store, and then restore the original screen?
32) How can I convert a TPU unit of one TP version to another?
33) Which error is e.g. Runtime error 205, etc
34) Why can't I open read-only files? I get "File access denied".
35) How do I obtain high and low parts of a byte variable?
36) How can I set a hi-intensity color background in the text mode?
37) Where can I find a program to convert (Turbo) Pascal to C?
38) How can I read input without echoing to the screen?
39) How can I edit the readln input stream?
40) How can I write (brand) something into my executables?
41) What is wrong with my program? It hangs without a clear pattern?
42) How do I convert a decimal word into a hexadecimal string, etc?
43) How to determine the last drive?
44) How can I put a running clock into my Turbo Pascal program?
45) How to establish if a name refers to a directory or not?
46) How does one disable alt-ctrl-del?
47) How can I test whether a file exists?
48) What is the name of the current Turbo Pascal program?
49) How is the code for rebooting the PC written in Turbo Pascal?
50) How can I write inline code?
51) I am running out of memory when compiling my large program.
52) How do I avoid scrolling in the last column of the last row?
53) How can one hide (or unhide) a directory using a TP program?
54) How do I test whether a file is already open in a TP program?
55) How can I test and convert a numerical string into a real?
56) How can I reverse a TP .EXE or .TPU back into source code?
57) How can I calculate the difference between two points of time?
58) Is a program running stand-alone or from within the IDE?
59) Please explain Turbo Pascal memory addressing to me.
60) How do I obtain a bit or bits from a byte, a word or a longint?
-------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:31 1993
Subject: Saving the screen
31. *****
Q: How does one store, and then restore the original screen?
A: Here is a simple outline for storing and restoring a text mode
screen. Note that the code below is incomplete in a sense that it
works for a color monitor only, because the monochrome screen
address is $B000:$0000.
For storing and restoring the graphics screen see Ohlsen & Stoker
(1989), Turbo Pascal Advanced Techniques, Que, pp 333-337.
uses Crt;
type ScreenType = array [1..4000] of byte; (* 2 x 80 x 25 *)
var ColorScreen : ScreenType Absolute $B800:$0000;
SavedScreen : ScreenType;
posx, posy : byte;
begin
SavedScreen := ColorScreen; (* Save the screen *)
posx := WhereX; posy := WhereY; (* Save the cursor position *)
writeln ('A simple demo storing and restoring the color text screen');
writeln ('By Prof. Timo Salmi, ts@uwasa.fi');
writeln; write ('Press <-'''); readln;
ColorScreen := SavedScreen; (* Restore the screen *)
GotoXY(posx,posy); (* Go to the stored cursor position *)
end.
If you would prefer not using the Crt unit, you can apply WHEREXFN,
WHEREYFN, and GOATXY from TSUNTG.TPU from /pc/ts/tspa33*.zip.
Likewise, if you wish to test for the monitor type, that is choose
between $B800:$0000 and $B000:$0000 bases, you can use
MONOFN "Is it a monochrome video adapter"
in the said units collection.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:32 1993
Subject: Converting TPUs
32. *****
Q: How can I convert a TPU unit of one TP version to another?
A: Forget it. In practical terms such a conversion is not on. The
Turbo Pascal TPU units are strictly version dependent. If there were
a working solution I assume we would have heard of it long since.
The hacks that have been tried won't solve this dilemma. For all
practical purposes you need the source code and the relevant
compiler version.
You may nevertheless wish to ascertain for which version a TPU
unit has been compiled. This is very simple. Just look at the first
four character of a TPU file. The codes are
TPU0 for 4.0
TPU5 for 5.0
TPU6 for 5.5
TPU9 for 6.0
TPUQ for 7.0 real mode
But don't go editing these. It will not get you anywhere.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:33 1993
Subject: Finding about runtime errors
33. *****
Q: Which error is e.g. Runtime error 205
A: Basically this is a case of RTFM (read the f*ing manual). But it
is very easy to find out even without resorting to the manual. Put
temporarily the statement RunError (205); as the first statement of
your program. Then run your program from the Turbo Pascal IDE, that
is from within the TP editor. The description of the error will
appear.
If you run a program from within a Turbo Pascal IDE, it is
advisable to turn on the debug options on. You'll get both the error
number and the description. Furthermore by pressing F1 after the
error you get its description in a more verbal format.
One further trick is to put "uses TSERR"; (Include verbal
run-time error messages) into your program. If you do that, the
run-time errors will be given with a verbal description not just as
a number. TSERR.TPU is part of my TPU collection /pc/ts/tspa*.zip.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:34 1993
Subject: Opening read-only files
34. *****
Q: Why can't I open read-only files? I get "File access denied".
A: The answer is rather simple, but it is not well displayed in the
manuals. In order to read a read-only file you have to set the
FileMode as 0 like below. Else you'll get runtime error 005 "File
access denied".
var f : text; (* Can be any file type *)
savefm : byte;
begin
savefm := FileMode; (* Save the current FileMode status *)
FileMode := 0; (* The default is 2 *)
assign (f, 'readonly.txt');
reset (f);
{ have your wicked ways }
close (f);
FileMode := savefm; (* Restore the original FileMode *)
end.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:35 1993
Subject: Getting a nybble from a byte
35. *****
Q: I have a variable of type BYTE and would like to extract two
numbers from it. (The first 4 bits making up number A, the second 4
bits making up number B). How can I extract these two numbers?
A: Ah, this questions bring back the good bad old days of the
Commodore C64 programming when bit operations were rather a rule
than an exception. Here is the solution.
function HIBYTEFN (x : byte) : byte;
begin
hibytefn := x Shr 4; (* Shift right by four bits *)
end;
{}
function LOBYTEFN (x : byte) : byte;
begin
lobytefn := x and 15; (* x and 00001111 *)
end;
From Patrick Taylor (exuptr@exu.ericsson.se): Ah, leave it to Timo
to come up with a different way! An other is (n div 16)
(n mod 16).
Patrick is right. But unless the compiler is optimized, the
former produces more efficient code. Not that it really makes any
practical difference whatsoever.
Of course the fastest code is produced using assembler as pointed
out by Maarten Pennings (maarten@cs.ruu.nl) who provided the
following inline example:
function high(b:byte):byte;
inline($58 { POP AX | AH=?, AL=b }
/$30/$e4 { XOR AH,AH | AH=0, AL=b }
/$b9/$04/$00 { MOV CX,0004 | AH=0, AL=b, CL=4 }
/$d3/$e8 { SHR AX,CL | AX=b shr 4 }
);
A2: Getting a word from a longint can alternatively be achieved
without any calculations by using a kind of typecasting. Below is
the code I have utilized in garbo.uwasa.fi:/pc/tspa*.zip.
(* Get the high-order word of the longint argument *)
function HIWORDFN (x : longint) : word;
type type1 = record
low : word;
high : word;
end;
var m1 : type1 absolute x;
begin
hiwordfn := m1.high;
end; (* hiwordfn *)
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:36 1993
Subject: Setting hi-intensity background
36. *****
Q: How can I set a hi-intensity color background in the text mode?
A: As you should know, the you can test for a blinking text for
example as follows.
uses Crt;
begin
TextColor (11 + 128); (* or LightCyan + Blink *)
TextBackground (Blue);
writeln ('What''s the catch?'); (* An aside, note the '' pair *)
end.
In the above, bit 7 (the 128) controls the blinking. If you have at
least an EGA, you can alter the interpretation of the highest text
color bit to denote a hi-intensity background, but then you lose the
the blinking. The following piece of code disables blinking,
enabling a hi-intensity background.
uses Dos;
var regs : registers;
begin
FillChar (regs, SizeOf(regs), 0); (* An initialization precaution *)
regs.ah := $10; (* Function $10 *)
regs.al := $03; (* Subfunction $03 *)
regs.bl := $00;
Intr ($10, regs); (* ROM BIOS video driver interrupt *)
end.
To enable blinking again, set regs.bl := $01; Any high-intensity
background you may have currently on the screen, will instantly
change into a blinking text a a low-intensity background.
A2: The previous answer assumes at least an EGA. Otherwise ports
must be accessed. This is both advanced and dangerous programming,
because errors in handling posts can do real harm. Besides it is
fair to require at least an EGA in writing modern programs, at least
for non-laptops, and on the latter the colors don't really matter
for CGA and below. Let's take a look, nevertheless, how this is done
for a CGA. Note that this won't work an an EGA and beyond, not at
least in my tests. For detecting the video adapter you have, see the
DetectGraph procedure in you Turbo Pascal manual.
First we need some basics from MEMORY.LST in Ralf Brown's
garbo.uwasa.fi:/pc/programming/inter38b.zip (or whatever version is
current):
Format of BIOS Data Segment at segment 40h:
63h WORD Video CRT controller base address: color=03D4h, mono=03B4h
65h BYTE Video current setting of mode select register 03D8h/03B8h
From David Jurgens's /pc/programming/helppc21.zip we see
3D0-3DF Color Graphics Monitor Adapter (ports 3D0-3DB are
write only, see 6845)
3D8 6845 Mode control register (CGA, EGA, VGA, except PCjr)
From Darryl Friesen's (friesend@jester.usask.ca) in comp.lang.pascal
we have, the following procedure, with my own added comments (* *).
procedure SetBlinkState (state : boolean);
var ModeRegPort : word;
ModeReg : byte;
begin
Inline($FA); { CLI } (* Interrupts off *)
ModeRegPort := MemW[$0040:$0063]+4; (* Typically $03D4+4 = $03D8 *)
ModeReg := Mem[$0040:$0065]; (* Typically 1001 *)
if state then (* Bit 5 controls blink enable *)
ModeReg := ModeReg or $20 (* $20 = 00100000 (base2) *)
else
ModeReg := ModeReg and $DF; (* $DF = 11011111 disable *)
Port[ModeRegPort] := ModeReg; (* Typically $9 = 00001001 *)
Mem[$0040:$0065] := ModeReg; (* or $29 = 00101001 *)
Inline($FB) { STI } (* Interrupts on *)
end;
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:37 1993
Subject: Pascal to C
37. *****
Q: Where can I find a program to convert (Turbo) Pascal to C?
A: This is a relevant question, but I have placed elsewhere the
tips on the "looking for a program" questions. Here are the
pointers to further pointers :-). (The FAQ versions might have been
updated since I wrote this.)
garbo.uwasa.fi:/pc/pd2/camfaq.zip
camfaq.zip comp.archives.msdos.(d/announce) FAQ (general finding)
:
garbo.uwasa.fi:/pc/pd2/tsfaqn39.zip
tsfaqn39.zip Questions from UseNet and Timo's answers
:
garbo.uwasa.fi:/pc/pd2/faquote.zip
faquote.zip Old information from tsfaq Frequently Asked Questions
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:38 1993
Subject: Turning off the input echo
38. *****
Q: How can I read input without echoing to the screen?
A: It is fairly simple. Study this example source code, with the
manual, if need be.
uses Crt;
var password : string;
{}
(* Read without echoing *)
procedure GETPASS (var s : string);
var key : integer;
ch : char;
begin
s := '';
repeat
ch := ReadKey; key := ord (ch);
case key of
0 : ch := ReadKey; (* Discard two-character keys, like F1 *)
13 : exit; (* Enter has been pressed *)
1..12,13..31,255 :; (* Discard the special characters *)
else s := s + ch;
end;
until false;
end; (* getpass *)
{}
(* The main program *)
begin
write ('Password: ');
GETPASS (password);
writeln;
writeln (password);
end.
{}
If you wish to be able to edit the input stream, like having the
BackSpace functional, that is more complicated, and is left as an
exercise after these basics. A hint: 8 : Delete (s, Length(s), 1);
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:39 1993
Subject: Input line-editing
39. *****
Q: How can I edit the readln input stream?
A: In practice, if you wish to use anything beyond simple the
BackSpace deleting, you'll have to build your own line editing
routines expanding on the code in the previous item. It is quite a
task, and you can alternatively find the preprogrammed routines in
my Turbo Pascal units tspa33*.zip (or whatever version number is
current).
EDRDEBLN Editable Readln with ctrl-c, break trapping, pre-fill etc
EDRDEFLN Editable Readln with recall, pre-fill, and insert toggle
EDRDLN Readln with line-editing potential (the simplest)
EDREABLN Edreadln with ctrl-c and break trapping
EDREADLN Editable Readln with recall, and insert toggle
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:40 1993
Subject: Executable branding
40. *****
Q: How can I write (brand) something into my executables?
Here is the actual question that led me to writing this item: 'I
am very interested in the .EXE "branding" techniques you use in
your TSUNTI unit. Would it be possible to get hold of the source
code for that unit, as it would save me from having to re-invent
the wheel?'
A: What you are referring to is
BRANDEXE Store information within your program's .exe file (MsDos 3.0+)
CHKSUMFN Checksum selftest to detect any tampering (MsDos 3.0+)
USECOUNT Get the number of times the program has been used
Sorry no, I don't want to distribute my /pc/turbopas/tspa33*.zip
source codes. Besides they would be less useful to you than you may
think because internally my programs are in Finnish, comments,
variable and procedure names, and all. But I can hopefully help you
by giving a reference to a similar code. Please see Ohlsen &
Stoker, Turbo Pascal Advanced Techniques, Que, 1989, p. 420.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:41 1993
Subject: Elusive, inconsistent errors
41. *****
Q: What is wrong with my program? It hangs without a clear pattern?
A: With experience one learns that some programming errors are very
elusive. I have many times seen users declaring that they have found
a bug in Turbo Pascal, but in the overwhelming majority of cases it
still is just a programming error, which just is more difficult to
find than the more clear-cut cases. When you have symptoms like your
program crashing from within the IDE, but working seemingly all
right when called as stand-alone, or something equally strange, you
might have one of the following problems.
- A variable or some variables in your code are uninitialized thus
getting random values, which differ depending on your environment.
- Your indexes are overflowing. Set on the range check {$R+}
directive for testing.
- An error in the pointer logic.
Normal debugging does not necessarily help in locating these errors
because one is easily led to debugging the wrong parts of one's
program. Especially the latter two reasons can cause errors which
seemingly have nothing to do with the actual cause. This results
from the fact that indexing and pointer errors can overwrite parts
of memory causing strange quirks in your program. If you have used
indexing with {$R-} or if you use pointer operations, sooner or
later you are bound to have these problems in developing your
applications.
See Edward Mitchell (1993), Borland Pascal Developer's Guide,
275-288 for common programming errors and especially the information
on memory clobbering, in a useful chapter on debugging Turbo Pascal
programs. You might also take a look at your Turbo Pascal User's
Guide. At least version 7.0 has an instructive general
categorization of errors on pages 76-77.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:42 1993
Subject: Converting the number base
42. *****
Q: How do I convert a decimal word into a hexadecimal string, etc?
A: Here is one possibility
function HEXFN (decimal : word) : string;
const hexDigit : array [0..15] of char = '0123456789ABCDEF';
begin
hexfn := hexDigit[(decimal shr 12)]
+ hexDigit[(decimal shr 8) and $0F]
+ hexDigit[(decimal shr 4) and $0F]
+ hexDigit[(decimal and $0F)];
end; (* hexfn *)
Here is another conversion example (from longint to binary string)
function LBINFN (decimal : longint) : string;
const BinDigit : array [0..1] of char = '01';
var i : byte;
binar : string;
begin
FillChar (binar, SizeOf(binar), ' ');
binar[0] := chr(32);
for i := 0 to 31 do
binar[32-i] := BinDigit[(decimal shr i) and 1];
lbinfn := binar;
end; (* lbinfn *)
For a full set of conversions, both from and to decimal, apply
TSUTNTB.TPU from garbo.uwasa.fi:/pc/ts/tspa*.zip.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:43 1993
Subject: Identifying the last drive
43. *****
Q: How to determine the last drive?
A: One way of doing that is utilizing the information in DPB, that
is the Drive Parameter Block, but that is rather complicated, so you
can find that without source code in garbo.uwasa.fi:/pc/ts/tspa*.zip
in the TSUNTH unit.
Another way is using interrrupt 21H, function 36H to detect if a
drive exists starting from the first drive letter. The code is given
below. The disadvantage of this method is that it does not
distinguish between real and substituted drives.
uses Dos;
function LASTDFN : char; (* Detect last harddisk letter *)
var regs : registers;
i : byte;
begin
i := 2;
repeat
Inc(i);
FillChar (regs, SizeOf(regs), 0);
regs.ah := $36;
regs.dl := i;
MsDos(regs);
until (regs.ax = $FFFF);
lastdfn := chr(i+63);
end; (* lastdfn *)
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:44 1993
Subject: Clock display in a TP program
44. *****
Q: How can I put a running clock into my Turbo Pascal program?
A: We are not speaking of a stand-alone TSR-clock (which is a
different task), but considering a clock that continuously displays
the time in some part of the output screen of your Turbo Pascal
program.
You might first want to read the earlier items about ReadKey
usages if you are not familiar with it (you probably are, because
you would not pose this advanced question if you were a novice). The
items are the unlikely "How do I disable or capture the break key in
Turbo Pascal?" and "How can I read input without echoing to the
screen?"
The general idea is to make the body of the program a repeat
until loop using ReadKey for input and updating the clock display
at suitable junctions within the loop. The scheme is thus something
like the following.
procedure showtime;
begin
{ if the second has changed, write the time }
end;
:
repeat
{ do whatever }
showtime;
if KeyPressed then
case ReadKey of
{ whatever }
{ exit rules }
end;
showtime;
:
showtime;
until false;
One trick of the trade is that you must not update your clock
each time the clock routine is encountered. You should test if the
second has changed, and update only then. Else you are liable to get
an annoying flicker in your clock.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:45 1993
Subject: Is a name a directory
45. *****
Q: How to establish if a name refers to a directory or not?
A: This question has turned out a bit more complicated than I first
thought. There are several methods, each with some catch. The first
is trying to open the name as a file and observing the IOResult. The
ISDIRFN function in garbo.uwasa.fi:/pc/ts/tspa*.zip TPU unit
TSUNTJ.TPU is based on this method. Unfortunately it is not always
stable. I have been reported problems in connection with DRDOS by
Richard Breuer (ricki@pool.informatik.rwth-aachen.de) who has
tested these routines.
The second method (ISDIR2FN) is based on the fact that the file
NUL exists in a directory if the directory exists.
The thrid method (ISDIR3FN) is a brute force method. It is given
below, since it is quite an instructive little exercise of Turbo
Pascal programming.
(* Search recursively through a drive's directories.
Auxiliary, recursive procedure for ISDIR3FN *)
procedure SEARCHDR (Path, FileSpec : string;
name : string;
var found : boolean);
var FileInfo : SearchRec;
begin
FindFirst (Path + '*.*', Directory, FileInfo);
while DosError = 0 do
begin
if ((FileInfo.Attr and Directory) > 0) and
(FileInfo.Name <> '.') and
(FileInfo.Name <> '..') then
begin
SEARCHDR (Path + FileInfo.Name + '\',
FileSpec,
name,
found);
if Path + FileInfo.Name + '\' = name then
found := true;
end;
FindNext (FileInfo);
end; {while}
end; (* searchdr *)
(* Does a name refer to a directory *)
function ISDIR3FN (name : string) : boolean;
var drive : char;
found : boolean;
begin
{... Default value ...}
isdir3fn := false;
{... Discard empty names ...}
if name = '' then exit;
{... Expand into a fully qualified name, makes it uppercase ...}
name := FExpand (name);
if name[Length(name)] <> '\' then name := name + '\';
{... Extract the drive letter from the name ...}
drive := UpCase (name[1]);
{... Check first for the root ...}
if drive + ':\' = name then
begin isdir3fn := true; exit; end;
{... Check the rest of the directories recursively ...}
found := false;
SEARCHDR (drive + ':\', '*.*', name, found);
isdir3fn := found;
end; (* isdir3fn *)
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:46 1993
Subject: Disabling alt-ctrl-del
46. *****
Q: How does one disable alt-ctrl-del?
A: I can only give a pointer to source code. Take a look at the
code by Mikko Hanninen in garbo.uwasa.fi:/pc/turbopas/cadthf10.zip.
I have utilized alt-ctrl-del disabling at least in one of my own
programs (PESTIKID.EXE). The code is not available, but the general
idea is replacing the old keyboard interrupt ($09) with a handler of
one's own. If the handler detects alt-ctrl-del, the keyboard is
reset, else the handler is chained back to the original interrupt.
The chaining requires a rather complicated inline procedure provided
in TurboPower Software's kit. An additional complication is that the
del keypress must be intercepted already at the relevant port $60,
and the alt and ctrl status must be tested, so that the rebooting
will not be invoked. Resetting the keyboard requires accessing the
$20 and $61 ports.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:47 1993
Subject: Does a file exist
47. *****
Q: How can I test whether a file exists?
A: There are several alternatives. Here is the most common with
example code. It recognizes also read-only, hidden and system files.
function FILEXIST (name : string) : boolean;
var fm : byte;
f : file;
b : boolean;
begin
fm := FileMode;
FileMode := 0;
assign (f, name);
{$I-} reset(f); {$I+}
b := IOResult = 0;
if b then close(f);
filexist := b;
FileMode := fm;
end;
A second alternative is
Uses Dos;
function FILEXIST (name : string) : boolean;
var f : file;
a : word;
begin
assign (f, name);
GetFAttr (f, a);
filexist := false;
if DosError = 0 then
if ((a and Directory) = 0) and ((a and VolumeId) = 0) then
filexist := true;
end;
A third alternative is
Uses Dos;
function FILEXIST (name : PathStr) : boolean;
begin
filexist := FSearch (name, '') <> '';
end;
A fourth alternative is the following. Be careful with this option,
since it works a bit differently from the others. It accepts wild
cards. Thus, for example FILEXIST('c:\autoexec.*') would be TRUE in
this method, while FALSE in all the above.
Uses Dos;
function FILEXIST (name : string) : boolean;
var f : SearchRec;
begin
filexist := false;
FindFirst (name, AnyFile, f);
if DosError = 0 then
if (f.attr <> Directory) and (f.attr <> VolumeId) then
filexist := true;
end;
A good variation from KDT@newton.national-physical-lab.co.uk of this
theme, disallowing wildcards:
function file_exists (fname :string) :boolean;
var f :searchrec;
begin
findfirst (fname, anyfile - directory - volumeid, f);
file_exists := (doserror + pos('*',fname) + pos('?',fname) = 0);
end;
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:48 1993
Subject: The current program name
48. *****
Q: What is the name of the current Turbo Pascal program?
A: The name of the currently executing Turbo Pascal program is in
ParamStr(0).
This was introduced in TP version 5.0, and as far as I recall at
least MsDos version 3.0 is required. For TP 4.0 you can use
"ParamStr0 The name of the program" from TSUNT45 in garbo.uwasa.fi:
/pc/ts/tspa3340.zip (or whatever the version number is the latest).
It is advisable to put the value into a string variable at be
beginning of the program before eny I/O takes place. Thus you might
wish to use:
var progname : string;
begin { the main program }
progname := ParamStr(0);
:
A bonus of this method is that you can access the individual
characters of progname (e.g. progname[1] for the drive) while that
is not possible to do for the ParamStr keyword.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:49 1993
Subject: How can a program reboot my PC?
49. *****
Q: How is the code for rebooting the PC written in Turbo Pascal?
A: This item draws from the information and the C-code example in
Stan Brown's comp.os.msdos.programmer FAQ, garbo.uwasa.fi:
/pc/doc-net/faqp9317.zip (at the time of writing this), from
memory.lst and interrup.b in /pc/programming/inter38b.zip, and from
/pc/programming/helppc21.zip. The Turbo Pascal code is my adaptation
of the C-code. It is not a one-to-one replica.
The usually advocated warm-boot method is storing $1234 in the
word at $0040:$0072 and jumping to address $FFFF:$0000. The problem
with this approach is that files must first be closed, potential
caches flushed. This is how to do this
procedure REBOOT;
label next;
var regs : registers;
i : byte;
ticks : longint;
begin
{... "press" alt-ctrl ...}
mem[$0040:$0017] := mem[$0040:$0017] or $0C; { 00001100 }
{... "press" del, try a few times ...}
for i := 1 to 10 do
begin
FillChar (regs, sizeOf(regs), 0); { initialize }
regs.ah := $4F; { service number }
regs.al := $53; { del key's scan code }
regs.flags := FCarry; { "sentinel for ignoring key" }
Intr ($15, regs);
{... check if the del key registered, if not retry ...}
if regs.flags and Fcarry > 0 then goto next;
{... waste some time, watch out for midnight ...}
ticks := MemL [$0040:$006C];
repeat until (MemL[$0040:$006C] - ticks > 3) or
(MemL[$0040:$006C] - ticks < 0)
end; {for}
exit;
next:
{... disk reset: writes all modified disk buffers to disk ...}
FillChar (regs, sizeOf(regs), 0);
regs.ah := $0D;
MsDos (regs);
{... set post-reset flag, use $0000 instead of $1234 for coldboot ...}
memW[$0040:$0072] := $1234;
{... jump to $FFFF:0000 BIOS reset ...}
Inline($EA/$00/$00/$FF/$FF);
end; (* reboot *)
One slight problem with this approach is that the keyboard intercept
interrupt $15 service $4F requires at least an AT according to
inter38b.zip. A simple test based on "FFFF:E byte ROM machine id"
(the previous definition is from helppc21.zip) is:
function ISATFN : boolean;
begin
case Mem[$F000:$FFFE] of
$FC, $FA, $F8 : isatfn := true;
else isatfn := false;
end; {case}
end; (* isatfn *)
For a more comprehensive test use CPUFN "Get the type of the
processor chip" from TSUNTH in garbo.uwasa.fi:/pc/ts/tspa*.zip or
see the TP + ASM code in Michael Ticher (1992), PC Intern System
Programming, pp. 725-727.
An addition by Per Bergland (d6caps@dtek.chalmers.se): I recently
downloaded the FAQ for this newsgroup, and studied the code for
rebooting a PC. The problem with that code (calling FFFF:0000) is
that it will not work in protected mode programs such as those
compiled for Windows or BP7 DPMI, or even in a DOS program run in a
Windows DOS session. The solution provided has been tested on
various COMPAQ PC:s, but I think it will work on any AT-class
machine. It involves using the 8042 keyboard controller chip output
pin 0, which is physically connected to the reset pin of the CPU.
There is unfortunately no way to perform a "warm" reboot this way,
and the warnings about disk caches etc apply to this code, too (see
FAQ). The code is written in BP7 assembly lingo, because that's what
I normally write code in, but anyone could rewrite it in C or high
level Pascal.
UNIT Reboot;
INTERFACE
procedure DoReboot;
IMPLEMENTATION
procedure DoReboot;assembler;
asm
cli
@@WaitOutReady: { Busy-wait until 8042 is ready for new command}
in al,64h { read 8042 status byte}
test al,00000010b { Bit 1 of status indicates input buffer full }
jnz @@WaitOutReady
mov al,0FEh { Pulse "reset" = 8042 pin 0 }
out 64h,al
{ The PC will reboot now }
end;
END.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:50 1993
Subject: Writing inline code
50. *****
Q: How can I write inline code?
A: In Turbo Pascal versions prior 6.0 assembler code could not be
directly included in the code. Instead one had to assemble the code
into inline statements. Consider the task of rebooting the PC
(without disk closing and cache flushing). The assemble code for
this is
mov ax,40
mov ds,ax
mov wo [72],1234
jmp FFFF:0000
To assemble this code into an inline statement write the following
file calling it e.g. debug.in. The empty line is important.
.... begin debug.in, cut here ....
a 100
mov ax,40
mov ds,ax
mov wo [72],1234
jmp FFFF:0000
u 100
q
.... end debug.in, cut here ....
Give the following command
debug < debug.in
You'll get
0E9E:0100 B84000 MOV AX,0040
0E9E:0103 8ED8 MOV DS,AX
0E9E:0105 C70672003412 MOV WORD PTR [0072],1234
0E9E:010B EA0000FFFF JMP FFFF:0000
This translates into
Inline ($B8/$40/$00/
$8E/$D8/
$C7/$06/$72/$00/$34/$12/
$EA/$00/$00/$FF/$FF);
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:51 1993
Subject: Out of memory in compiling
51. *****
Q: I am running out of memory when compiling my large program. What
can I do?
A: If you are compiling your program from within the IDE (the
Integrated Development Environment) then select the Option from the
main menu, choose the Compiler item and set the Link buffer to
Disk. (Also make the Compile option Destination to be Disk).
If this is not sufficient, next resort to using the TPC command
line version of the Turbo Pascal compiler instead of the IDE. Use
the "Link buffer on disk" option.
Divide your program into units. It is advisable anyway for
modularity when your program size grows.
If you have extended memory, instead of TURBO.EXE use TPX.EXE, if
you have TP 7.0. If you are into protected mode programming then use
Borland Pascal BP 7.0.
A2: If you would prefer compiling your program from within the IDE
but cannot do it for the above reason (or if you would prefer to
compile your program from within your favorite editor instead of the
TP IDE) you can use the following trick. If your editor has a macro
language like most good editors do, the assign a hotkey macro that
compiles the current file with the TPC. If you are using SemWare's
QEdit editor you'll find such a macro in garbo.uwasa.fi:/pc/ts/
tsqed17.zip ("Macros and configurations for QEdit text-editor").
Also your editor must be swapped to disk during the compilation
if memory is critical. There is a very good program for doing that:
/pc/sysutil/shroom2d.zip ("Shell Room, Swap to disk when shelling to
application"). For example I invoke the QEdit editor with using the
following batch:
c:\tools\shroom -s r:\cmand -z 1024 c:\qedit\q %1 %2 %3 %4 %5 %6 %7
You'll find more about the switches in the Shell Room documentation.
The -s switch designates the swap destination (my r:\cmand directory
is on my ramdisk). The -z switch sets the shell environment size.
An unfortunate part is that the TP 5.0 Turbo Pascal IDE is about
the only program I know that is not amenable the to Shell Room
utility, so you cannot utilize Shell Room to swap the TP IDE to
disk. Blessfully, at least TP 7.0 no more has this problem.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:52 1993
Subject: Last position write woes
52. *****
Q: How do I avoid scrolling in the last column of the last row?
A: If you use write or writeln at the last column of the last row
(usually 80,25) the screen will scroll. If you wish to avoid the
scrolling you'll have to use an alternative write that does not move
the cursor. Here is a procedure to write without moving the cursor
uses Dos;
procedure WriteChar (Character : char; fgColor, bgColor : byte);
var r : registers;
begin
FillChar (r, SizeOf(r), 0);
r.ah := $09;
r.al := ord(Character);
r.bl := (bgColor shl 4) or fgColor;
r.cx := 1; { Number of repeats }
Intr ($10, r);
end; (* writechar *)
Thus, if you wish to write to the last column of the last row, you
must first move the cursor to that position. That can be done in
alternative ways. One might get there by having written previously
on the screen (with writeln and write routines) until one is in that
position. Another alternative is using GoToXY(80,20), but then you
have to use the Crt unit. If you don't want to use it, then you can
move the cursor by employing "GOATXY As the ordinary GoToXY but no
Crt unit required" from garbo.uwasa.fi:/pc/ts/ tspa*.zip.
There is an alternative interrupt service ($0A) which does the
same as service $09, but uses the default colors instead. Just
substitute $0A for $09, and leave the r.bl assignment out of the
WriteChar routine.
Another option for writing anyhere on the screen without
affecting the cursor is using direct screen writes:
uses Dos;
procedure WriteChar (c : char; x, y : byte; fg, bg : byte);
var vidstart : word;
regs : registers;
begin
FillChar (regs, SizeOf(regs), 0);
regs.ah := $0F;
Intr ($10, regs); { Color or MonoChrome video adapter }
if regs.al = 7 then vidstart := $B000 else vidstart := $B800;
mem[vidstart:((y-1)*80+x-1)*2] := ord(c);
mem[vidstart:((y-1)*80+x-1)*2+1] := (bg shl 4) or fg;
end;
To write to the last position simply apply e.g.
WriteChar ('X', 80, 25, 14, 0); { Yellow on black }
The foreground (fg) and the background (bg) color codes are
Black = 0
Blue = 1
Green = 2
Cyan = 3
Red = 4
Magenta = 5
Brown = 6
LightGray = 7
DarkGray = 8
LightBlue = 9
LightGreen = 10
LightCyan = 11
LightRed = 12
LightMagenta = 13
Yellow = 14
White = 15
Blink = 128
Yet another option is the following, but it needs the Crt unit. On
the other hand, it uses the default color. This is quite a good and
easy solution. I captured this fairly frequent idea from a posting
by Robert Buergi (nbuero@hslrswi.hasler.ascom.ch).
uses Crt;
procedure WriteToCorner (c : char);
begin
Inc (WindMax);
GotoXY (80, 25);
write (c);
Dec (WindMax);
end; (* writeToCorner *)
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:53 1993
Subject: Hiding a directory
53. *****
Q: How can one hide (or unhide) a directory using a TP program?
A: Here is the code using interrupt programming. Incidentally,
since MsDos 5.0 the attrib command can be used to hide and unhide
directories.
(* Hide a directory. Before using it would be prudent to check
that the directory exists, and that it is a directory.
With a contribution from Jan Nielsen jak@hdc.hha.dk
Based on information from Duncan (1986), p. 410 *)
procedure HIDE (dirname : string);
var regs : registers;
begin
FillChar (regs, SizeOf(regs), 0); { standard precaution }
dirname := dirname + #0; { requires ASCII strings }
regs.ah := $43; { function }
regs.al := $01; { subfunction }
regs.ds := Seg(dirname[1]); { point to the name }
regs.dx := Ofs(dirname[1]);
regs.cx := 2; { set bit 1 on } { to unhide set regs.cx := 0 }
Intr ($21, regs); { call the interrupt }
if regs.Flags and FCarry <> 0 then { were we successful }
writeln ('Failed to hide');
end; (* hide *)
A2: An alternative method by Dr. Abimbola Olowofoyeku
laa12@seq1.keele.ac.uk. No paths.
procedure HIDE (dirname : string);
var FileInfo : searchRec;
f : file;
begin
FindFirst (dirname, Directory, FileInfo);
while DosError = 0 do
begin
assign (f, FileInfo.Name);
SetFAttr (f, Hidden);
FindNext (FileInfo);
end;
end; (* hide *)
{}
procedure UNHIDE (dirname : string);
var FileInfo : searchRec;
f : file;
begin
FindFirst (dirname, AnyFile, FileInfo);
while DosError = 0 do
begin
assign (f, FileInfo.Name);
SetFAttr (f, Archive);
FindNext (FileInfo);
end;
end; (* unhide *)
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:54 1993
Subject: Testing file opened status
54. *****
Q: How do I test whether a file is already open in a TP program?
A: This question is best answered by providing the code:
uses Dos;
{... for non-text files ...}
function ISFOPEN (var filePointer : file) : boolean;
begin
isfopen := FileRec(filePointer).mode <> FmClosed;
end;
{}
{... for text files ...}
function ISTOPEN (var filePointer : text) : boolean;
begin
istopen := TextRec(filePointer).mode <> FmClosed;
end;
{}
procedure TEST; { Testing a non-text file }
const name = 'R:\TMP';
var f : file;
begin
Assign (f, name);
writeln ('File ', name, ' is open is ', ISFOPEN(f));
{$I-} rewrite (f); {$I+}
if IOResult <> 0 then
begin
writeln ('Failed to open ', name);
exit;
end;
writeln ('File ', name, ' is open is ', ISFOPEN(f));
close(f);
writeln ('File ', name, ' is open is ', ISFOPEN(f));
end;
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:55 1993
Subject: From string to real
55. *****
Q: How can I test and convert a numerical string into a real?
A: An easy task in Turbo Pascal but in standard Pascal this
frequent task is much trickier. Here are both the versions for
general edification :-).
(* Convert and test a numerical string with Turbo Pascal *)
function DIGVALFN (mj : string; var ok : boolean) : real;
var k : integer;
x : real;
begin
Val (mj, x, k);
ok := k = 0;
if ok then digvalfn := x else digvalfn := 0;
end; (* digvalfn *)
{}
(* Convert and test a numerical string with standard Pascal routines only *)
procedure DIGVAL (mj : string; var number : real; var ok : boolean);
label 1;
var il, lenl, pl, kl1, kl2 : integer;
nrol : boolean;
numberdl : real;
begin
ok := true; lenl := Length (mj); nrol := false; pl := 0; number := 0.0;
if lenl = 0 then ok := false;
for il:=2 to lenl do if (mj[il]='-') or (mj[il]='+') then ok := false;
for il:=1 to lenl do
case mj[il] of
'0'..'9','+','-','.' : ; else ok := false;
end;
for il:=1 to lenl do
case mj[il] of '0'..'9' : begin nrol := true; goto 1; end; end;
1: if nrol = false then ok := false;
for il:=1 to lenl do if mj[il] = '.' then pl := pl + 1;
if pl > 1 then ok := false;
kl1:=1; kl2:=lenl+1; if (mj[1]='-') or (mj[1]='+') then kl1 := 2;
for il:=1 to lenl do if mj[il] = '.' then kl2 := il;
if kl2-kl1 > 38 then ok := false;
if ok then
begin
number:=0; numberdl:=0;
for il:=kl1 to kl2-1 do number := (ord(mj[il])-48)+10*number;
if kl2 < lenl+1 then
for il:=lenl downto kl2+1 do
numberdl := (ord(mj[il])-48)/10+numberdl/10;
number := number + numberdl;
if mj[1]='-' then number := -number;
end; {if ok}
end; (* digval *)
{}
procedure TEST;
var s : string; r : real; ok : boolean;
begin
s := '123.41';
r := DIGVALFN (s, ok);
if ok then writeln (r) else writeln ('Error in ', s);
DIGVAL (s, r, ok);
if ok then writeln (r) else writeln ('Error in ', s);
end;
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:56 1993
Subject: Decompiling a TP .EXE
56. *****
Q: How can I reverse a TP .EXE or .TPU back into source code?
A: This is simply asking too much. You cannot decompile a TP
program in a manner that would give you back the original source.
This method of reverse engineering is not on in actual practice.
Quoting Jeroen Pluimers (jeroenp@dragons.nest.nl) "During the
compilation, important information get's lost about variables,
types, identifiers etc. Writing a Pascal Decompiler is impossible.
The best you can achieve is a disassembler that can help you
recognize some Pascal statements."
You might note that this question somewhat resembles another
frequent question "How can I convert a TPU unit of one TP version to
another?" which cannot be solved without the original source code.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:57 1993
Subject: Calculating date/time differences
57. *****
Q: How can I calculate the difference between two points of time?
A: This is an unconfirmed answer so be a little careful with it.
But at the very least it shows some interesting information about
Turbo Pascal date/time conventions and how to declare and initialize
typed constants if they are records.
program TimDifTest;
uses Dos;
const a : DateTime
= (year:1992; month:10; day:24; hour:5; min:29; sec:38);
b : DateTime
= (year:1993; month:11; day:23; hour:6; min:30; sec:51);
var aLong, bLong, cLong : longint;
c : DateTime;
begin
PackTime (a, aLong);
PackTime (b, bLong);
cLong := bLong - aLong;
UnpackTime (cLong, c);
writeln (c.year-1980, ' ', c.month, ' ', c.day, ' ',
c.hour, ' ', c.min, ' ', c.sec);
end.
More generally than for dates between 1980 and 2079, or for more
accurate results, the difference between two date/times can be
calculated using Zeller's congruence (see the item "I want code that
gives the weekday of the given date"). First calculate Zeller's for
both the dates, convert them, and the hour, min, and sec into
seconds, subtract, and convert back.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:58 1993
Subject: Stand-alone or from IDE
58. *****
Q: Is a program running stand-alone or from within the IDE?
A: Not all questions have an answer yet. I posed this question to
the UseNet newsgroup comp.lang.pascal, but we have not found an
answer that would be general for all MsDos versions. The closest we
have comes from dmurdoch@mast.queensu.ca Duncan Murdoch (naturally
:-). I have done some slight editing of Duncan's solution.
uses Dos;
type Pchar = ^Char;
function Asciiz2Str (p : Pchar) : string;
var
result : string;
len : byte;
begin
len := 0;
while (p^ <> #0) and (len < 255) do
begin
inc(len);
result[len] := p^;
inc(longint(p));
end;
result[0] := chr(len);
Asciiz2Str := result;
end;
{}
var parentSeg : ^word;
p : pchar;
begin
if swap(DosVersion) < $0400 then
writeln ('Requires Dos 4.0+')
else begin
parentSeg := ptr (prefixSeg, $16);
p := ptr (ParentSeg^-1, 8);
writeln ('I was launched by ', Asciiz2Str(p));
end;
end.
Another suggestion has been that the contents of ParamStr(0) would
show the launching program. I tested this on several configurations
and TP versions and found no evidence that the contention would
hold.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:59 1993
Subject: Memory Addressing
59. *****
Q: Please explain Turbo Pascal memory addressing to me.
A: This is far from an easy question, but let's see what we can do.
The origins of the difficulties are in the design of the 8086 chip
which still restricts all Turbo Pascal applications (which contrary
to Borland Pascal use the original real mode). The 8086 (aka real
mode) addressing is based on 16-bit registers. As you probably know
2^16 is 65536 which means that you cannot directly point to all
addresses of the lower and upper memory, which ranges from 0 to
1048575 (2^20-1). Thus all the memory addresses are pointed to into
two parts in TP programs, the segment and the offset. The following
example of the PC's memory illustrates.
Decimal Hexa-
address decimal Segment Offset What
0 $00000 $0000 $0000 Conventional memory starts
1043 $00413 $0040 $0013 Base memory in Kb, a word
655359 $9FFFF $9000 $FFFF Conventional memory ends
655360 $A0000 $A000 $0000 Upper memory begins
1048575 $FFFFF $F000 $FFFF Upper memory ends
To exemplify, let's look at some alternative ways we could access
the information about the amount of the base memory. It is very
straight-forward, since in a PC that information is at the fixed
memory location show by the above table. We know this in advance.
Using direct memory accessing we could write
var memsize : word;
memsize := MemW [$0040:$0013];
writeln (memsize);
{.. or ..}
var memsize : word absolute $0040:$0013;
writeln (memsize);
If you are not familiar with the true meaning of pointers, they may
feel confusing, but what they basically are is just what the name
indicates, pointers to memory locations. Study the following
example.
var memsizePtr : ^word; { A pointer to a word }
begin
memsizePtr := ptr ($40, $13); { Assign the pointer a value }
writeln (memsizePtr^); { Write what is in the address }
end. { that was pointed to }
This was relatively simple, since we knew in advance the location of
the information. Lets look at a case where we do not know that.
Consider
var x : word;
begin
x := 1223;
writeln (x);
end.
We have a variable x somewhere in the memory, and we can refer to it
without ever needing to know where the variable actually is in the
memory. But how does one find out if one for some reason wants or
needs to know?
var x : word;
Segment : word;
Offset : word;
y : ^word;
begin
x := 1223;
writeln (x);
Segment := Seg(x);
Offset := Ofs(x);
writeln ('Variable x is at $', HEXFN(Segment), ':$', HEXFN(Offset));
{... one test to ensure that the value really is in there ...}
writeln (MemW [Segment:Offset]);
{... another test to demonstrate that the value really is in there ...}
y := Addr(x);
writeln (y^);
end.
Next consider
var xPtr : ^word;
Segment : word;
Offset : word;
yPtr : ^word;
begin
xPtr^ := 1223;
writeln (xPtr^);
Segment := Seg(xPtr^);
Offset := Ofs(xPtr^);
writeln ('$', HEXFN(Segment), ':$', HEXFN(Offset));
{... a test to ensure that the value really is in there ...}
yPtr := Ptr (Segment, Offset);
writeln (yPtr^);
end.
A further aspect of pointers is that you can utilize them to put a
variables onto the heap instead of the data segment so that you
won't run so easily out of space.
var xPtr : ^word;
begin
{ Put it onto the heap }
New (xPtr);
xPtr^ := 1223;
writeln (xPtr^);
{ Get rid of it }
Dispose (xPtr); xPtr := nil;
readln;
end.
Let us return to the addressing. The formulas for converting between
the addresses (sent in by Duncan Murdoch) are
Physical := longint(segment)*16 + offset;
{}
Segment := Physical div 16;
Offset := Physical mod 16; { This gives the normalized form }
There are multiple Segment:Offset pairs that refer to the same
address, e.g. $0000:$0413 and $0040:$0013. The normalized addresses,
with the offset in the range of 0 to $F, are, however, unique. An
example $0041:$0003.
--------------------------------------------------------------------
From ts@uwasa.fi Sun Dec 26 00:00:60 1993
Subject: Getting a bit from a byte
60. *****
Q: How do I obtain a bit or bits from a byte, a word or a longint?
A: For bit operations think of the variable as a binary number
instead of a decimal. Consider for example
var x : word;
x := 219;
In binary presentation it is
The word 0000 0000 1101 1011
Position in the word FEDC BA98 7654 3210
Say you need the value of bit 6 (the seventh bit) in the word. You
can "and" the following words
0000 0000 1101 1011 (219)
0000 0000 0100 0000 ( 64)
In decimal TP notation this amounts to
var b : word;
b := x and 64;
The value of b is now
0000 0000 0100 0000 ( 64)
To get the bit value (0 or 1) you need to shift the result right by
six steps, that this the expression becomes the often seen but
cryptic
b := (x and 64) shr 6;
which means that the value of b is finally 1 in this example.
Ok, but what then if you need the combined value of bits six and
seven. The answer is evident if you consider the binary presentation
0000 0000 1101 1011 (219)
0000 0000 1100 0000 (192)
hence
b := (x and 192) shr 6;
which will give 3 as it should.
So far, so good. What if you need to turn on bit nine in a word
without interfering with the other bits. The binary presentation,
again, is the key. You'll have to "or" the following
0000 0000 1101 1011 (219)
0000 0010 0000 0000 (512)
that is
x := x or 512;
This results to
0000 0010 1101 1011 (731)
What if you wish to turn off, say bit 6, in
0000 0000 1101 1011 (219)
1111 1111 1011 1111 (65471)
This is achieved by
x := 219;
x := x and 65471;
This results to
0000 0000 1001 1011 (155)
Consider the following application as an example. The number of a
PC's floppy disk drives (minus one) is stored in bits 6 and 7 in a
word returned by interrupt $11. This is the code to find out how
many disk drives a PC has.
uses Dos;
function NrOfFDiskDrives : byte;
var regs : registers;
begin
Intr ($11, regs);
NrOfFDiskDrives := ((regs.ax and 192) shr 6) + 1;
end;
A tip from Duncan Murdoch. You might wish to predefine the
following constants for easier handling
const bit0 = 1;
bit2 = 2;
bit3 = 4;
:
bit31 = 32768;
Finally, you also might want to look at the item "Getting a nybble
from a byte".
--------------------------------------------------------------------